﻿
using System;
using System.Net;
using System.Windows;
using System.Windows.Browser;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Xml;
using System.Xml.Linq;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Windows.Threading;



namespace RWG.Silverlight.Components.SPDiscussion
{
    public class DiscusssionManager
    {
        /// <summary>
        /// This is a delagate that will be called once the discussions have been received.
        /// The GotDiscussions callback will be called on a non-UI thread, so to do 
        /// databinding it should:
        ///  this.Dispatcher.BeginInvoke(delegate()
        ///   {
        ///      xxx.ItemsSource = discussions;
        /// });
        /// </summary>
        public delegate void GotDiscussionHeaders(ObservableCollection<DiscussionHeader> discussions);
        public delegate void GotDisccussionMessages(ObservableCollection<DiscussionHeader> discussions);
        SPLists.ListsSoap listsService;
        /// <summary>
        /// Initializes a new instance of the <see cref="Discussions"/> class, creating
        /// a new WCF service to access Sharepoint lists.
        /// </summary>
        public DiscusssionManager()
        {
            Uri SourceURI=Application.Current.Host.Source;
            String ServiceUriFormat = "{0}://{1}:{2}/_vti_bin/Lists.asmx";
            String ServiceUri = String.Format(ServiceUriFormat,SourceURI.Scheme, SourceURI.Host,SourceURI.Port);

            listsService = new SPLists.ListsSoapClient("ListsSoap", ServiceUri);
            
            
        }
        public class Person
        {
            int personID;
            public int PersonID
            {
                get { return personID; }
                set { personID = value; }
            }
            string personName;
            public string PersonName
            {
                get { return personName; }
                set { personName = value; }
            }
            public Person(string personString)
            {
                //   person looks like this 1;#Gove Russell
                string[] parts = personString.Split(';');
                personID = Convert.ToInt32(parts[0]);
                personName = parts[1].Substring(1);
            }
        }

        /// <summary>
        /// This is the data class used to store info about a single disussion.
        /// </summary>
        public class DiscussionHeader : INotifyPropertyChanged
        {
            public DiscussionHeader()
            {
                messages = new ObservableCollection<DiscussionMessage>();
            }
            DateTime? discussionLastUpdated;
            public DateTime? DiscussionLastUpdated
            {
                get { return discussionLastUpdated; }
                set
                {
                    discussionLastUpdated = value;
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs("DiscussionLastUpdated"));
                }
            }
            int itemChildCount;
            public int ItemChildCount
            {
                get { return itemChildCount; }
                set
                {
                    itemChildCount = value;
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs("ItemChildCount"));
                }
            }
            int id;
            public int ID
            {
                get { return id; }
                set
                {
                    id = value;
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs("ID"));
                }
            }
            Person author;
            public Person Author
            {
                get { return author; }
                set
                {
                    author = value;
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs("Author"));
                }
            }
            string title;
            public string Title
            {
                get
                {
                    return title;
                }
                set
                {
                    title = value;
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs("Title"));
                }
            }
            string listRef;
            public string ListRef
            {
                get { return listRef; }
                set
                {
                    listRef = value;
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs("ListRef"));
                }
            }
            string webID;
            public string WebID
            {
                get { return webID; }
                set
                {
                    webID = value;
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs("WebID"));
                }
            }
            string body;
            public string Body
            {
                get { return body; }
                set
                {
                    body = value;
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs("Body"));
                }
            }
            string thread;
            public string Thread
            {
                get { return thread; }
                set
                {
                    thread = value;
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs("Threading"));
                }
            }
            public string ParentThread
            {
                //chop of the last five bytes to get the parent thread
                get { return thread.Substring(0, thread.Length - 10); }

            }
            string fileRef;
            public string FileRef
            {
                get { return fileRef; }
                set
                {
                    fileRef = value;
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs("Messages"));
                }
            }
            private ObservableCollection<DiscussionMessage> messages;
            public ObservableCollection<DiscussionMessage> Messages
            {
                get { return messages; }
                set
                {
                    messages = value;
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs("Messages"));
                }
            }

            #region INotifyPropertyChanged Members

            public event PropertyChangedEventHandler PropertyChanged;

            #endregion
        }
     
        private class GotDiscussionHeadersSavedState
        {
            public string listname;
            public ObservableCollection<DiscusssionManager.DiscussionHeader> discussionList;
            public string webID;
            public Dispatcher dispatcher;

            public GotDiscussionHeadersSavedState(string listname, string webID, ObservableCollection<DiscusssionManager.DiscussionHeader> discussions, Dispatcher dispatcher)
            {
                this.discussionList = discussions;
                this.listname = listname;
                this.webID = webID;
                this.dispatcher = dispatcher;

            }


        }
        public class DiscussionMessage : INotifyPropertyChanged
        {

            public DiscussionMessage()
            {
                childMessages = new ObservableCollection<DiscussionMessage>();
            }
            private ObservableCollection<DiscussionMessage> childMessages;
            public ObservableCollection<DiscussionMessage> ChildMessages
            {
                get { return childMessages; }
                set
                {
                    childMessages = value;
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs("ChildMessages"));
                }
            }
            DateTime? discussionLastUpdated;
                        public DateTime? DiscussionLastUpdated
            {
                get { return discussionLastUpdated; }
                set
                {
                    discussionLastUpdated = value;
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs("DiscussionLastUpdated"));
                }
            }
            int itemChildCount;
            public int ItemChildCount
            {
                get { return itemChildCount; }
                set
                {
                    itemChildCount = value;
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs("ItemChildCount"));
                }
            }
            int id;
            public int ID
            {
                get { return id; }
                set
                {
                    id = value;
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs("ID"));
                }
            }
            Person author;
            public Person Author
            {
                get { return author; }
                set
                {
                    author = value;
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs("Author"));
                }
            }
            string title;
            public string Title
            {
                get
                {
                    return title;
                }
                set
                {
                    title = value;
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs("Title"));
                }
            }
            string listRef;
            public string ListRef
            {
                get { return listRef; }
                set
                {
                    listRef = value;
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs("ListRef"));
                }
            }
            string webID;
            public string WebID
            {
                get { return webID; }
                set
                {
                    webID = value;
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs("WebID"));
                }
            }
            string body;
            public string Body
            {
                get { return body; }
                set
                {
                    body = value;
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs("Body"));
                }
            }
            string thread;
            public string Thread
            {
                get { return thread; }
                set
                {
                    thread = value;
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs("Threading"));
                }
            }
            public string ParentThread
            {
                //chop of the last five bytes to get the parent thread
                get { return thread.Substring(0, thread.Length - 10); }

            }
            string fileRef;
            public string FileRef
            {
                get { return fileRef; }
                set
                {
                    fileRef = value;
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs("Messages"));
                }
            }
            List<DiscussionHeader> messages;

            public List<DiscussionHeader> Messages
            {
                get { return messages; }
                set
                {
                    messages = value;
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs("Messages"));
                }
            }

            #region INotifyPropertyChanged Members

            public event PropertyChangedEventHandler PropertyChanged;

            #endregion
        }
       
        private class GotDiscussionMessagesSavedState
        {
                
            public string listname;
            public ObservableCollection<DiscusssionManager.DiscussionMessage> messageList;
            public string webID;
            public Dispatcher dispatcher;
            public string thread;
            public GotDiscussionMessagesSavedState(string listname, string webID, ObservableCollection<DiscusssionManager.DiscussionMessage> messages, Dispatcher dispatcher,string thread)
            {
                this.messageList = messages;
                this.listname = listname;
                this.webID = webID;
                this.dispatcher = dispatcher;
                this.thread=thread;
            }


        }
        public void GetDiscussionHeaders(string listName, ObservableCollection<DiscusssionManager.DiscussionHeader> discussions, string webID, Dispatcher dispatcher)
        {
            SPLists.GetListItemsRequest request = new SPLists.GetListItemsRequest();
            request.Body = new SPLists.GetListItemsRequestBody();
            request.Body.listName = listName;
            request.Body.viewName = null;
            request.Body.query = null;
            request.Body.viewFields = System.Xml.Linq.XElement.Parse(
                @"<ViewFields> 
                    <FieldRef Name='Author'/> 
                    <FieldRef Name='DiscussionLastUpdated' /> 
                    <FieldRef Name='Body' />
                    <FieldRef Name='LinkDiscussionTitleNoMenu' />
                    <FieldRef Name='ID' />
                    <FieldRef Name='FileRef' />
                    <FieldRef Name='ItemChildCount' />
                    <FieldRef Name='Threading'/>
                    </ViewFields>"
                );
            request.Body.rowLimit = null;
            request.Body.queryOptions = System.Xml.Linq.XElement.Parse(
                @"<QueryOptions>
                    <IncludeMandatoryColumns>TRUE</IncludeMandatoryColumns>
                    <DateInUtc>TRUE</DateInUtc></QueryOptions>"
                );
            request.Body.webID = webID;
            listsService.BeginGetListItems(request, new AsyncCallback(OnGetDiscussionHeadersComplete), new GotDiscussionHeadersSavedState(listName, webID, discussions, dispatcher));
        }

        /// <summary>
        /// Called when the call to BeginGetListItems has finished.
        /// </summary>
        /// <param name="res">The IAsyncResult</param>
        private void OnGetDiscussionHeadersComplete(IAsyncResult res)
        {
            SPLists.GetListItemsResponse resp = listsService.EndGetListItems(res);
            System.Xml.Linq.XElement result = resp.Body.GetListItemsResult;
            GotDiscussionHeadersSavedState callbakInfo = (GotDiscussionHeadersSavedState)res.AsyncState;
            XNamespace ns = "#RowsetSchema";
            var query = from item in result.Descendants()
                        where item.Name == ns + "row"
                        select new DiscusssionManager.DiscussionHeader
                        {
                            Author = new Person(item.Attribute("ows_Author").Value),
                            Title = item.Attribute("ows_LinkDiscussionTitleNoMenu").Value,
                            DiscussionLastUpdated = item.Attribute("ows_DiscussionLastUpdated") != null ?
                            Convert.ToDateTime(item.Attribute("ows_DiscussionLastUpdated").Value) :
                            (DateTime?)null,
                            ItemChildCount = GetSPFieldNumeric(item.Attribute("ows_ItemChildCount").Value),
                            ID = Convert.ToInt32(item.Attribute("ows_ID").Value),
                            Body = item.Attribute("ows_Body").Value,
                            FileRef = GetSPFieldString(item.Attribute("ows_FileRef").Value),
                            Thread = item.Attribute("ows_Threading").Value,
                            ListRef = callbakInfo.listname,
                            WebID = callbakInfo.webID,
                        };
            callbakInfo.dispatcher.BeginInvoke(delegate()
            {
                foreach (DiscussionHeader hdr in query)
                {
                    callbakInfo.discussionList.Add(hdr);
                }
            });



        }

        public void GetDiscussionMessages(string listName, DiscussionHeader discussionHeader, string webID,Dispatcher dispatcher)
        {
            SPLists.GetListItemsRequest request = new SPLists.GetListItemsRequest();
            request.Body = new SPLists.GetListItemsRequestBody();
            request.Body.listName = listName;
            request.Body.viewName = null;
            request.Body.viewFields = System.Xml.Linq.XElement.Parse(
                @"<ViewFields> 
                    <FieldRef Name='Author'/> 
                    <FieldRef Name='Created' /> 
                    <FieldRef Name='Body' />
                    <FieldRef Name='Title' />
                    <FieldRef Name='ID' />
                    <FieldRef Name='FileRef' />
                    <FieldRef Name='ItemChildCount'/>
                    <FieldRef Name='Threading'/>
                    </ViewFields>"
                );
            request.Body.rowLimit = null;
            request.Body.queryOptions = System.Xml.Linq.XElement.Parse(@"
                 <QueryOptions>  
                    <IncludeMandatoryColumns>TRUE</IncludeMandatoryColumns>
                    <DateInUtc>TRUE</DateInUtc>
                    <Folder>" + discussionHeader.FileRef + @"</Folder>
                  </QueryOptions>
                ");
            request.Body.webID = webID;
            listsService.BeginGetListItems(request, new AsyncCallback(OnGetDiscussionMessagesComplete), new GotDiscussionMessagesSavedState(listName, webID, discussionHeader.Messages, dispatcher,discussionHeader.Thread));
        }
        private void OnGetDiscussionMessagesComplete(IAsyncResult res)
        {
            SPLists.GetListItemsResponse resp = listsService.EndGetListItems(res);
            System.Xml.Linq.XElement result = resp.Body.GetListItemsResult;
            GotDiscussionMessagesSavedState callBackInfo = (GotDiscussionMessagesSavedState)res.AsyncState;
            XNamespace ns = "#RowsetSchema";
            var query = from item in result.Descendants()

                        where item.Name == ns + "row"
                 && (item.Attribute("ows_Threading").Value.ToString().Substring(0, item.Attribute("ows_Threading").Value.ToString().Length - 10) == callBackInfo.thread)
                        select new DiscusssionManager.DiscussionMessage
                        {
                            Author = new Person(item.Attribute("ows_Author").Value),
                            Title = null,
                            DiscussionLastUpdated = item.Attribute("ows_Created") != null ?
                            Convert.ToDateTime(item.Attribute("ows_Created").Value) :
                            (DateTime?)null,
                            ItemChildCount = GetSPFieldNumeric(item.Attribute("ows_ItemChildCount").Value),
                            ID = Convert.ToInt32(item.Attribute("ows_ID").Value),
                            Body = item.Attribute("ows_Body").Value,
                            FileRef = GetSPFieldString(item.Attribute("ows_FileRef").Value),

                            ListRef = callBackInfo.listname,
                            Thread = item.Attribute("ows_Threading").Value,
                            WebID = callBackInfo.webID,
                        };
            try
            {
                List<DiscussionMessage> messages = query.ToList<DiscussionMessage>();
                ObservableCollection<DiscussionMessage> discussionsColll = messages.ToObservableCollection<DiscussionMessage>();

                callBackInfo.dispatcher.BeginInvoke(delegate()
                {
                    foreach (DiscussionMessage msg in  query)
                    {

                        callBackInfo.messageList.Add(msg);
                        FillChildren(msg.ChildMessages, msg.Thread, result.Descendants(),callBackInfo.listname,callBackInfo.webID);
                    }
                });
            }
            catch (Exception e)
            {

                throw e;
            }
        }

        private void FillChildren(ObservableCollection<DiscussionMessage> children, string threadID, IEnumerable<XElement> iEnumerable,string listName,string webID)
        {
            XNamespace ns = "#RowsetSchema";
            var query = from item in iEnumerable

                        where item.Name == ns + "row"
                 && (item.Attribute("ows_Threading").Value.ToString().Substring(0, item.Attribute("ows_Threading").Value.ToString().Length - 10) == threadID)
                        select new DiscusssionManager.DiscussionMessage
                        {
                            Author = new Person(item.Attribute("ows_Author").Value),
                            Title = null,
                            DiscussionLastUpdated = item.Attribute("ows_DiscussionLastUpdated") != null ?
                            Convert.ToDateTime(item.Attribute("ows_DiscussionLastUpdated").Value) :
                            (DateTime?)null,
                            ItemChildCount = GetSPFieldNumeric(item.Attribute("ows_ItemChildCount").Value),
                            ID = Convert.ToInt32(item.Attribute("ows_ID").Value),
                            Body = item.Attribute("ows_Body").Value,
                            FileRef = GetSPFieldString(item.Attribute("ows_FileRef").Value),

                            ListRef = listName,
                            Thread = item.Attribute("ows_Threading").Value,
                            WebID = webID,
                        };
            
                List<DiscussionMessage> messages = query.ToList<DiscussionMessage>();
                ObservableCollection<DiscussionMessage> discussionsColll = messages.ToObservableCollection<DiscussionMessage>();

                    foreach (DiscussionMessage msg in query)
                    {

                        children.Add(msg);
                        FillChildren(children, msg.Thread, iEnumerable,listName,webID);
                    }
               
            }
        

       
        #region Utility methods
        public string[] SplitSPField(string spField)
        {
            string[] temp = spField.Split(';');
            temp[1] = temp[1].Substring(1);
            return temp;

        }
        public string GetSPFieldString(string spField)
        {
            string[] temp = SplitSPField(spField);
            return temp[1];
        }
        public int GetSPFieldNumeric(string spField)
        {
            string[] temp = SplitSPField(spField);
            int theNumber = Convert.ToInt32(temp[1]);
            return theNumber;
        }
        #endregion Utility methods
    }
}

  